#include "Color.h"
#include "MathUtil.h"
#include <math.h>

Color::Color(void)
{
}

Color::Color( double r, double g, double b )
{
	this->r = (float) r;
	this->g = (float) g;
	this->b = (float) b;
}

Color::Color( Color* c )
{
	r = c->r;
	g = c->g;
	b = c->b;
}

Color::Color( int rgb )
{
	r = ((rgb >> 16) & 0xFF) / 255.0f;
	g = ((rgb >> 8) & 0xFF) / 255.0f;
	b = ((rgb) & 0xFF) / 255.0f;

}
Color::~Color(void)
{
}

Color* Color::set( double r, double g, double b )
{
	this->r = (float) r;
	this->g = (float) g;
	this->b = (float) b;
	return this;
}

Color* Color::set( Color c )
{
	r = c.r;
	g = c.g;
	b = c.b;
	return this;
}

Color* Color::setRGB( int rgb )
{
	r = ((rgb >> 16) & 0xFF) / 255.0f;
	g = ((rgb >> 8) & 0xFF) / 255.0f;
	b = ((rgb) & 0xFF) / 255.0f;
	return this;
}

Color* Color::setRGBE( int rgbe )
{
	int e = rgbe & 0xFF;
	if (e != 0) {
		float f = 1.0f;
		e = e - (128 + 8);
		if (e > 0)
			for (int i = 0; i < e; i++)
				f *= 2.0f;
		else
			for (int i = 0; i < -e; i++)
				f *= 0.5f;
		int ir = (rgbe >> 24) & 0xFF;
		int ig = (rgbe >> 16) & 0xFF;
		int ib = (rgbe >> 8) & 0xFF;
		r = f * (ir + 0.5f);
		g = f * (ig + 0.5f);
		b = f * (ib + 0.5f);
	} else
		r = g = b = 0.0f;
	return this;
}

double Color::getLuminance()
{
	return 0.2989 * r + 0.5866 * g + 0.1145 * b;
}

double Color::getMin()
{
	return MathUtil::min(r, g, b);
}

double Color::getMax()
{
	return MathUtil::max(r, g, b);
}

double Color::getAverage()
{
	return (r + g + b) / 3.0;
}

int Color::toRGB()
{
	// gamma correct and pack into 32bits
	int ir = (int) (pow((float)r, (float)(1.0 / GAMMA)) * 255.0);
	int ig = (int) (pow((float)g, (float)(1.0 / GAMMA)) * 255.0);
	int ib = (int) (pow((float)b, (float)(1.0 / GAMMA)) * 255.0);
	ir = MathUtil::clamp(ir, 0, 255);
	ig = MathUtil::clamp(ig, 0, 255);
	ib = MathUtil::clamp(ib, 0, 255);
	return (ir << 16) | (ig << 8) | ib;

}

int Color::toRGBE()
{
	// encode the color into 32bits while preserving HDR using Ward's RGBE technique
	float v = MathUtil::max(r, g, b);
	if (v < 1e-32)
		return 0;
	// get mantissa and exponent
	float m = v;
	int e = 0;
	if (v > 1.0f) {
		while (m > 1.0f) {
			m *= 0.5f;
			e++;
		}
	} else if (v <= 0.5f) {
		while (m <= 0.5f) {
			m *= 2.0f;
			e--;
		}
	}
	v = m * 255.0f / v;
	int c = (e + 128);
	c |= (int) (r * v) << 24;
	c |= (int) (g * v) << 16;
	c |= (int) (b * v) << 8;
	return c;
}

Color* Color::add( Color c )
{
	r += c.r;
	g += c.g;
	b += c.b;
	return this;
}

Color* Color::add( Color c1, Color c2, Color* dest )
{
	dest->r = c1.r + c2.r;
	dest->g = c1.g + c2.g;
	dest->b = c1.b + c2.b;
	return dest;
}

Color* Color::add( Color c1, Color c2 )
{
	return Color::add(c1, c2, new Color());
}

Color* Color::sub( Color c )
{
	r -= c.r;
	g -= c.g;
	b -= c.b;
	return this;
}

Color* Color::sub( Color c1, Color c2, Color* dest )
{
	dest->r = c1.r - c2.r;
	dest->g = c1.g - c2.g;
	dest->b = c1.b - c2.b;
	return dest;
}

Color* Color::sub( Color c1, Color c2 )
{
	return Color::sub(c1, c2, new Color());
}

Color* Color::mul( Color c )
{
	r *= c.r;
	g *= c.g;
	b *= c.b;
	return this;
}

Color* Color::mul( Color c1, Color c2, Color* dest )
{
	dest->r = c1.r * c2.r;
	dest->g = c1.g * c2.g;
	dest->b = c1.b * c2.b;
	return dest;
}

Color* Color::mul( Color c1, Color c2 )
{
	return Color::mul(c1, c2, new Color());
}

Color* Color::mul( double s )
{
	r *= s;
	g *= s;
	b *= s;
	return this;
}

Color* Color::mul( double s, Color c, Color* dest )
{
	dest->r = (float) s * c.r;
	dest->g = (float) s * c.g;
	dest->b = (float) s * c.b;
	return dest;
}

Color* Color::mul( double s, Color c )
{
	return Color::mul(s, c, new Color());
}

Color* Color::div( Color c )
{
	r /= c.r;
	g /= c.g;
	b /= c.b;
	return this;
}

Color* Color::div( Color c1, Color c2, Color* dest )
{
	dest->r = c1.r / c2.r;
	dest->g = c1.g / c2.g;
	dest->b = c1.b / c2.b;
	return dest;
}

Color* Color::div( Color c1, Color c2 )
{
	return Color::div(c1, c2, new Color());
}